using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Expression.Service;

namespace T.FAS.Runtime.Action.Engine
{
    /// <summary>
    /// 自定义返回值数据扩展逻辑
    /// </summary>
    internal class UserDefineResultHandler
    {
        /// <summary>
        /// 根据自定义的返回值数据扩展逻辑进行数据组装
        /// </summary>
        /// <param name="result"></param>
        /// <param name="context"></param>
        /// <param name="tResult"></param>
        /// <param name="schema"></param>
        /// <returns> 当业务开发人员按照FasActionCommonResult组织出了数据，返回true，否则返回false </returns>
        /// <exception cref="FasException"></exception>
        public static bool ConvertToTResultByMappingJS(object result,ActionExecContext context, TCommonResult<JObject> tResult)
        {
            var action = context.MetaData as ActionMetaData;
            if (action == null)
                return false;
            if (string.IsNullOrEmpty(action.ResultMappingJS))
                return false;

            var isList = action.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false;

            bool isUserDefineException = false;
            var commonResult = new FasActionCommonResult();
            try
            {
                if (context.FASContext == null)
                    context.FASContext = new FASContext();
                context.FASContext.SetValue(ActionRTPConst.FasActionReturnData, result);
                commonResult = ExecuteResultMappingJS(context, action, commonResult);
                if (commonResult != null)
                {
                    if (string.IsNullOrEmpty(commonResult.state) || commonResult.state.Equals(1)|| commonResult.state.Equals("1"))
                    {
                        if (isList)
                        {
                            var listData = commonResult.data as JToken;
                            if (listData != null)
                            {
                                var listRecord = new TArray<JObject>(tResult.Schema.Entity);
                                foreach (var item in listData)
                                {
                                    listRecord.Add(item as JObject);
                                }
                                tResult.List = listRecord;
                            }
                            tResult.Count = commonResult.total;
                        }
                        else
                        {
                            tResult.Record = commonResult.data as JObject;
                        }
                        return true;
                    }
                    else
                    {
                        isUserDefineException = true;
                    }
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                var errMsg = "";
                if (!string.IsNullOrEmpty(ex.Message))
                    errMsg = ex.Message;
                else if (ex.InnerException != null && !string.IsNullOrEmpty(ex.InnerException.Message))
                    errMsg = ex.Message;
                else
                    errMsg = ex.InnerException == null ? Convert.ToString(ex) : Convert.ToString(ex.InnerException);
                throw new FasException(ActionExecuteExceptionCode.SGActionExecuteError, errMsg);
            }
            if(isUserDefineException)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, commonResult.errmsg);
            return false;
        }

        /// <summary>
        /// 根据自定义的返回值数据扩展逻辑进行数据组装
        /// </summary>
        /// <param name="result"></param>
        /// <param name="context"></param>
        /// <param name="tResult"></param>
        /// <param name="schema"></param>
        /// <returns> 当业务开发人员按照FasActionCommonResult组织出了数据，返回true，否则返回false </returns>
        /// <exception cref="FasException"></exception>
        public static bool ConvertToTResultByMappingJS(object result, ActionExecContext context, TCommonResult<TRecord> tResult)
        {
            var action = context.MetaData as ActionMetaData;
            if (action == null)
                return false;
            if (string.IsNullOrEmpty(action.ResultMappingJS))
                return false;

            var isList = action.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false;
            if (string.IsNullOrEmpty(action.ResultMappingJS))
                return false;
            bool isUserDefineException = false;
            var commonResult = new FasActionCommonResult();
            try
            {
                if (context.FASContext == null)
                    context.FASContext = new FASContext();
                context.FASContext.SetValue(ActionRTPConst.FasActionReturnData,result);
                commonResult = ExecuteResultMappingJS(context, action, commonResult);
                if (commonResult != null)
                {
                    if (string.IsNullOrEmpty(commonResult.state) || commonResult.state.Equals(1) || commonResult.state.Equals("1"))
                    {
                        if (isList)
                        {
                            var listData = commonResult.data as JToken;
                            if (listData != null)
                            {
                                var listRecord = new TArray<TRecord>(tResult.Schema.Entity);
                                foreach (var item in listData)
                                {
                                    listRecord.Add(TRecord.BuildRecord(item as JObject));
                                    //listRecord.Add(new TRecord(new Dictionary<string, object>() { { BasicTypeData.DefaultPropertyName, item } }));
                                }
                                tResult.List = listRecord;
                            }
                            tResult.Count = commonResult.total;
                        }
                        else
                        {
                            tResult.Record = new TRecord(new Dictionary<string, object>() { { BasicTypeData.DefaultPropertyName, commonResult.data } });
                        }
                        return true;
                    }
                    else
                    {
                        isUserDefineException = true;
                    }
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                var errMsg = "";
                if (!string.IsNullOrEmpty(ex.Message))
                    errMsg = ex.Message;
                else if (ex.InnerException != null && !string.IsNullOrEmpty(ex.InnerException.Message))
                    errMsg = ex.Message;
                else
                    errMsg = ex.InnerException == null ? Convert.ToString(ex) : Convert.ToString(ex.InnerException);
                throw new FasException(ActionExecuteExceptionCode.SGActionExecuteError, errMsg);
            }
            if (isUserDefineException)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, commonResult.errmsg);
            return false;
        }

        private static FasActionCommonResult ExecuteResultMappingJS(ActionExecContext context, ActionMetaData action, FasActionCommonResult commonResult)
        {
            var paramList = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

            var inParamsAndVariable = action.MethodParams.FindAll(p => p.ParamType == ParamType.In || p.ParamType == ParamType.Variable);
            if (inParamsAndVariable != null && inParamsAndVariable.Count > 0)
            {
                inParamsAndVariable.ForEach(v =>
                {
                    paramList[v.Code] = v.ParamValue == null ? v.DefaultValue : v.ParamValue;
                });
            }
            context.FASContext.SetValue(ActionRTPConst.FasActionCommonResult, new FasActionCommonResult());
            var args = new FasActionResultHandlerEventArgs();
            args.FASReturnData = context.FASContext.GetValue(ActionRTPConst.FasActionReturnData);
            paramList["e"] = args;
            var mappingResult = ExprService.GetInstance.Execute(action.ResultMappingJS, context.FASContext, paramList);
            if (mappingResult.State == ResultState.Fail)
            {
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.UserDefineResultHandler_ExecuteResultMappingJS,mappingResult?.RowNum,mappingResult?.ErrMsg));
            }
            //兼容返回值扩展脚本全是注释时，执行见过返回false的情况
            if (mappingResult.Result == null) return null;
            var commonResultObj = context.FASContext.GetValue(ActionRTPConst.FasActionCommonResult);
            if (commonResultObj != null)
            { 
                commonResult = JsonConvert.DeserializeObject<FasActionCommonResult>(JsonConvert.SerializeObject(commonResultObj));
                if (string.IsNullOrEmpty(commonResult.state) || commonResult.state.Equals("-1"))
                {
                    commonResult = JsonConvert.DeserializeObject<FasActionCommonResult>(JsonConvert.SerializeObject(mappingResult.Result));
                }
            }
            return commonResult;
        }
    }
}
